package org.andengine.entity.util;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.opengl.GLES20;

import org.andengine.engine.camera.Camera;
import org.andengine.entity.Entity;
import org.andengine.opengl.util.GLHelper;
import org.andengine.opengl.util.GLState;

import java.nio.IntBuffer;

/**
 * (c) 2010 Nicolas Gramlich
 * (c) 2011 Zynga Inc.
 *
 * @author Nicolas Gramlich
 * @since 15:27:22 - 10.01.2011
 */
public class ScreenGrabber extends Entity {
    // ===========================================================
    // Constants
    // ===========================================================

    // ===========================================================
    // Fields
    // ===========================================================

    private int mGrabX;
    private int mGrabY;
    private int mGrabWidth;
    private int mGrabHeight;

    private boolean mScreenGrabPending;
    private IScreenGrabberCallback mScreenGrabCallback;

    // ===========================================================
    // Constructors
    // ===========================================================

    // ===========================================================
    // Getter & Setter
    // ===========================================================

    // ===========================================================
    // Methods for/from SuperClass/Interfaces
    // ===========================================================

    private static Bitmap grab(final int pGrabX, final int pGrabY, final int pGrabWidth, final int pGrabHeight) {
        final int[] pixelsRGBA_8888 = new int[pGrabWidth * pGrabHeight];
        final IntBuffer pixelsRGBA_8888_Buffer = IntBuffer.wrap(pixelsRGBA_8888);

        // TODO Check availability of OpenGL and GLES20.GL_RGBA combinations that require less conversion operations.
        GLES20.glReadPixels(pGrabX, pGrabY, pGrabWidth, pGrabHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, pixelsRGBA_8888_Buffer);

		/* Convert from RGBA_8888 (Which is actually ABGR as the whole buffer seems to be inverted) --> ARGB_8888. */
        final int[] pixelsARGB_8888 = GLHelper.convertRGBA_8888toARGB_8888(pixelsRGBA_8888);

        return Bitmap.createBitmap(pixelsARGB_8888, pGrabWidth, pGrabHeight, Config.ARGB_8888);
    }

    @Override
    protected void onManagedDraw(final GLState pGLState, final Camera pCamera) {
        if (this.mScreenGrabPending) {
            try {
                final Bitmap screenGrab = ScreenGrabber.grab(this.mGrabX, this.mGrabY, this.mGrabWidth, this.mGrabHeight);

                this.mScreenGrabCallback.onScreenGrabbed(screenGrab);
            } catch (final Exception e) {
                this.mScreenGrabCallback.onScreenGrabFailed(e);
            }

            this.mScreenGrabPending = false;
        }
    }

    @Override
    protected void onManagedUpdate(final float pSecondsElapsed) {
        /* Nothing */
    }

    // ===========================================================
    // Methods
    // ===========================================================

    @Override
    public void reset() {
        /* Nothing */
    }

    public void grab(final int pGrabWidth, final int pGrabHeight, final IScreenGrabberCallback pScreenGrabCallback) {
        this.grab(0, 0, pGrabWidth, pGrabHeight, pScreenGrabCallback);
    }

    public void grab(final int pGrabX, final int pGrabY, final int pGrabWidth, final int pGrabHeight, final IScreenGrabberCallback pScreenGrabCallback) {
        this.mGrabX = pGrabX;
        this.mGrabY = pGrabY;
        this.mGrabWidth = pGrabWidth;
        this.mGrabHeight = pGrabHeight;
        this.mScreenGrabCallback = pScreenGrabCallback;

        this.mScreenGrabPending = true;
    }

    // ===========================================================
    // Inner and Anonymous Classes
    // ===========================================================

    public static interface IScreenGrabberCallback {
        // ===========================================================
        // Constants
        // ===========================================================

        // ===========================================================
        // Methods
        // ===========================================================

        public void onScreenGrabbed(final Bitmap pBitmap);

        public void onScreenGrabFailed(final Exception pException);
    }
}
